package com.tenclass.common.interceptor;

import cn.ilanxin.common.error.ContextExceptionV2;
import cn.ilanxin.common.type.v2.Result;
import com.google.gson.internal.$Gson$Types;
import feign.FeignException;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpMessageConverterExtractor;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

import static java.lang.String.format;

/**
 * feign 自定义解码类
 *
 * @author YIJIUE
 * @since 1.1.0
 */
public class FeignDecodeInterceptor implements Decoder {

    private final ObjectFactory<HttpMessageConverters> messageConverters;

    public FeignDecodeInterceptor(ObjectFactory<HttpMessageConverters> messageConverters) {
        this.messageConverters = messageConverters;
    }

    @Override
    public Object decode(Response response, Type type) throws IOException, FeignException {
        if (response.status() == 200) {
            boolean flag = false;
            if (!StringUtils.isEmpty(response.headers().get("Encode-f"))) {
                flag = true;
                type = $Gson$Types.newParameterizedTypeWithOwner(null, Result.class, type);
            }

            HttpMessageConverterExtractor<?> extractor = new HttpMessageConverterExtractor<>(type, this.messageConverters.getObject().getConverters());
            Object object = extractor.extractData(new FeignResponseAdapter(response));
            if (flag && (object instanceof Result)) {
                Result<?> result = (Result<?>) object;
                if (result.getCode() != 0) {
                    throw new ContextExceptionV2(result.getCode(), null, result.getMsg());
                }
                return result.getData();
            }
            return object;
        }

        throw new DecodeException(response.status(),
                format("%s is not a type supported by this decoder.", type), response.request());
    }

    private static final class FeignResponseAdapter implements ClientHttpResponse {

        private final Response response;

        private FeignResponseAdapter(Response response) {
            this.response = response;
        }

        @Override
        public HttpStatus getStatusCode() {
            return HttpStatus.valueOf(this.response.status());
        }

        @Override
        public int getRawStatusCode() {
            return this.response.status();
        }

        @NonNull
        @Override
        public String getStatusText() {
            return this.response.reason();
        }

        @Override
        public void close() {
            try {
                this.response.body().close();
            } catch (IOException ex) {
                // Ignore exception on close...
            }
        }

        @NonNull
        @Override
        public InputStream getBody() throws IOException {
            return this.response.body().asInputStream();
        }

        @Override
        public HttpHeaders getHeaders() {
            HttpHeaders httpHeaders = new HttpHeaders();
            for (Map.Entry<String, Collection<String>> entry : this.response.headers().entrySet()) {
                httpHeaders.put(entry.getKey(), new ArrayList<>(entry.getValue()));
            }
            return httpHeaders;
        }

    }
}
